package com.ruoyi.yuyuan.Utils;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.ruoyi.yuyuan.DTO.UserDTO;
import com.ruoyi.yuyuan.Entity.User;
import com.ruoyi.yuyuan.Service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import static com.ruoyi.yuyuan.Utils.RedisConstants.LOGIN_USER_TOKEN_KEY;

/**
 * @Author: kestrel
 * @Version:1.0
 * @Date: 2024/5/24
 */
@Component
public class AuthorityInterceptor implements HandlerInterceptor {

    private final IUserService userService;

    private final StringRedisTemplate stringRedisTemplate;

    public AuthorityInterceptor(IUserService userService, StringRedisTemplate stringRedisTemplate) {
        this.userService = userService;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                             Object object) throws Exception {
        String token = httpServletRequest.getHeader("Authorization");
        //从http请求关中取出token
        if (token == null) {
            throw new RuntimeException("请重新登录！");
        }
        String userId;
        String session_key;
        try {
            userId = JWT.decode(token).getKeyId();
            httpServletRequest.setAttribute("currentUser", userId);
            try {
                session_key = JWT.decode(token).getClaim("session_key").as(String.class);
                httpServletRequest.setAttribute("sessionKey", session_key);
            } catch (Exception ignored) {
            }
            if (!stringRedisTemplate.opsForHash().entries(LOGIN_USER_TOKEN_KEY + userId).isEmpty()){
                Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(LOGIN_USER_TOKEN_KEY + userId);
                //验证密码
                UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
                checkTokenPassword(userDTO.getPassword(),token);
                UserHolder.saveUser(userDTO);
                //浮动有效期
                Random random = new Random();
                stringRedisTemplate.expire(LOGIN_USER_TOKEN_KEY+token,10060 + random.nextInt(20), TimeUnit.MINUTES);
                return true;
            }
            User user = userService.getById(userId);
            if (user == null) {
                throw new RuntimeException("用户不存在，请重新登录");
            }
            UserDTO userDTO = new UserDTO(user.getId(),user.getName(),user.getIcon(),user.getPassword());
            //验证密码
            checkTokenPassword(userDTO.getPassword(),token);

            Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                    CopyOptions.create().setIgnoreNullValue(true)
                            .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));

            Random random = new Random();
            stringRedisTemplate.opsForHash().putAll(LOGIN_USER_TOKEN_KEY + userId, userMap);
            stringRedisTemplate.expire(LOGIN_USER_TOKEN_KEY + userId,10060 + random.nextInt(20),TimeUnit.MINUTES);
            UserHolder.saveUser(new UserDTO(user.getId(),user.getName(),user.getIcon(),user.getPassword()));

        } catch (JWTDecodeException j) {
            throw new RuntimeException("401");
        }
        return true;
    }
    /**
     * 避免内存泄露
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserHolder.removeUser();
    }

    public void checkTokenPassword(String password, String token){
        //验证密码
        JWTVerifier jwtverifier = JWT.require(Algorithm.HMAC256(password)).build();
        try{
            jwtverifier.verify(token);
        }
        catch (JWTVerificationException e){
            throw new RuntimeException("401");
        }
    }
}
